package com.sunchangming.stmdb;

import java.nio.ByteBuffer;
import java.nio.file.StandardOpenOption;

public class PageManager {
	static class DBMeta extends Page {
		private long firstFree = 2;
		private long maxUsedPage;
		private long lastLogid;

		DBMeta(byte[] data, long pageno) {
			super(data, pageno);
			com.sunchangming.stmdb.proto.Bpage.FileMeta meta;
			try {
				meta = com.sunchangming.stmdb.proto.Bpage.FileMeta.parseFrom(data);
			} catch (java.io.IOException ex) {
				throw new RuntimeException("载入meta page失败");
			}
			this.firstFree = meta.getFirstFree();
			this.lastLogid = meta.getLastLogid();
			this.maxUsedPage = meta.getMaxUsedPage();
		}

		public long getFirstFree() {
			return firstFree;
		}

		public long getMaxUsedPage() {
			return maxUsedPage;
		}

		@Override
		void onMarshal() throws java.io.IOException {
			if (!this.isDirty())
				return;
			com.sunchangming.stmdb.proto.Bpage.FileMeta.newBuilder().setFirstFree(firstFree)
					.setMaxUsedPage(maxUsedPage).setLastLogid(lastLogid).build().writeTo(this.getOutputStream());
		}

		public void setFirstFree(long firstFree) {
			if (firstFree != this.firstFree) {
				this.firstFree = firstFree;
				setDirty();
			}
		}

		public void setMaxUsedPage(long maxUsedPage) {
			if (this.maxUsedPage != maxUsedPage) {
				this.maxUsedPage = maxUsedPage;
				setDirty();
			}
		}

		public long incMaxUsedPage() {
			setDirty();
			return ++this.maxUsedPage;
		}

	}

	private java.nio.channels.FileChannel file;
	private String fileName;
	private LogFile lf;
	private static String extension = ".db";
	private DBMeta meta;

	public PageManager(final String fileName) throws java.io.IOException {
		this.fileName = fileName;
		final java.nio.file.Path path = java.nio.file.Paths.get(fileName + extension);
		this.file = java.nio.channels.FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE,
				StandardOpenOption.CREATE);
		lf = new LogFile(java.nio.file.Paths.get(fileName + ".log"));
		meta = (DBMeta) loadPage(0, false, Page.T_DBMeta);
	}

	public void writePage() {
		//TODO:先写日志文件。
		//this.lf.write(data);
		//然后写数据文件。
		//this.file.write(src, position);
		//然后写metapage ? 更新logid
	}

	Page allocNewPage(byte type) {
		long pageno=this.meta.incMaxUsedPage();
		byte[] data=new byte[Page.pagesize];
		data[0]=type;
		return Page.createByType(data[0], data, pageno).setDirty();
	}

	/**
	 * 第一个page的pageno是0
	 * 
	 * @param pageno
	 * @param isDirect
	 *            目前没用。请都传入false
	 */
	Page loadPage(long pageno, boolean isDirect, byte type) throws java.io.IOException {
		ByteBuffer bb;
		if (isDirect)
			bb = ByteBuffer.allocateDirect(Page.pagesize);
		else
			bb = ByteBuffer.allocate(Page.pagesize);
		int bytesReaded = this.file.read(bb, pageno * Page.pagesize);
		if(bytesReaded<0) //Pageno超出范围。
			return null;
		if (bytesReaded != Page.pagesize)
			throw new RuntimeException("读取文件失败");
		byte[] data = java.util.Arrays.copyOf(bb.array(), bb.position());
		if (data[0] != type)
			throw new RuntimeException("wrong type!");
		return Page.createByType(data[0], data, pageno);
	}
}
